package org.jflame.commons.reflect;

import org.jflame.commons.util.ArrayHelper;
import org.jflame.commons.util.StringHelper;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;

/**
 * 反射工具类
 * 
 * @see org.apache.commons.lang3.reflect.FieldUtils
 * @author zyc
 */
public final class ReflectionHelper {

    /**
     * 获取指定类<strong>的首个泛型接口</strong>的所有泛型参数的实际类型，如无法找到返回null
     * <p>
     * 注:clazz必须是指定了具体泛型类型,如果clazz是泛型类则会抛出类型转换异常
     * 
     * @param clazz 待查找类型,实现了某泛型接口的类
     * @return
     */
    @SuppressWarnings({ "unchecked" })
    public static <T> Class<T> getInterfaceGenericType(final Class<?> clazz) {
        Type[] params = getInterfaceGenericTypes(clazz);
        return ArrayUtils.isEmpty(params) ? null : (Class<T>) params[0];
    }

    /**
     * 获取指定类<strong>的所有泛型接口</strong>的所有泛型参数的实际类型
     * <p>
     * 注:clazz必须是指定了具体泛型类型,如果clazz是泛型类则会抛出类型转换异常
     * 
     * @param clazz
     * @return
     */
    public static Type[] getInterfaceGenericTypes(final Class<?> clazz) {
        Type[] types = clazz.getGenericInterfaces();
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] params = ((ParameterizedType) type).getActualTypeArguments();
                return params;
            }
        }
        return null;
    }

    /**
     * 获取指定类的父类的首个泛型参数的类型，如无法找到返回null
     * 
     * @param clazz 待查找类型
     * @return
     */
    @SuppressWarnings({ "unchecked","rawtypes" })
    public static <T> Class<T> getSuperClassGenericType(final Class clazz) {
        return (Class<T>) getSuperClassGenericType(clazz, 0);
    }

    /**
     * 获取指定类的父类的指定索引泛型参数的类型.无泛型或索引超出返回null <br>
     * 
     * @param clazz 待查找类型
     * @param index 多个泛型参数时的索引,0开始
     * @return 索引处的泛型参数class对象
     */
    @SuppressWarnings("rawtypes")
    public static Class<?> getSuperClassGenericType(final Class clazz, int index) {
        if (index < 0) {
            index = 0;
        }

        Type genType = clazz.getGenericSuperclass();
        if (genType instanceof ParameterizedType) {
            Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
            if (params.length <= index) {
                return (Class<?>) params[index];
            }
        }

        return null;
    }

    /**
     * 获取指定类所有泛型父类和泛型接口
     *
     * @param clazz 类
     * @return 泛型父类或接口数组
     */
    public static ParameterizedType[] getGenericTypes(final Class<?> clazz) {
        List<ParameterizedType> result = new ArrayList<>();
        // 泛型父类（父类及祖类优先级高）
        Type genericSuper = clazz.getGenericSuperclass();
        if (null != genericSuper && !Object.class.equals(genericSuper)) {
            final ParameterizedType parameterizedType = toParameterizedType(genericSuper);
            if (null != parameterizedType) {
                result.add(parameterizedType);
            }
        }

        // 泛型接口
        final Type[] genericInterfaces = clazz.getGenericInterfaces();
        if (ArrayHelper.isNotEmpty(genericInterfaces)) {
            for (final Type genericInterface : genericInterfaces) {
                final ParameterizedType parameterizedType = toParameterizedType(genericInterface);
                if (null != parameterizedType) {
                    result.add(parameterizedType);
                }
            }
        }
        return result.toArray(new ParameterizedType[0]);
    }

    public static ParameterizedType toParameterizedType(final Type type) {
        return toParameterizedType(type, 0);
    }

    /**
     * 将{@link Type} 转换为{@link ParameterizedType}
     *
     * @param type {@link Type}
     * @param interfaceIndex 实现的第几个接口
     * @return {@link ParameterizedType}
     */
    public static ParameterizedType toParameterizedType(final Type type, final int interfaceIndex) {
        if (type instanceof ParameterizedType) {
            return (ParameterizedType) type;
        }

        if (type instanceof Class) {
            final ParameterizedType[] generics = getGenericTypes((Class<?>) type);
            if (generics.length > interfaceIndex) {
                return generics[interfaceIndex];
            }
        }

        return null;
    }

    /**
     * 生成一个方法的签名字符串
     * 
     * @param method
     * @return
     */
    public static String toSignatureString(Method method) {
        StringJoiner joiner = new StringJoiner(":");
        joiner.add(method.getDeclaringClass()
                .getName())
                .add(method.getName())
                .add(appendType(method.getReturnType()));
        Class<?>[] parametersTypes = method.getParameterTypes();
        if (parametersTypes.length > 0) {
            StringJoiner paramJoiner = new StringJoiner(",");
            for (int i = 0; i < parametersTypes.length; i++) {
                paramJoiner.add(appendType(parametersTypes[i]));
            }
            joiner.merge(paramJoiner);
        }
        return joiner.toString();
    }

    private static String appendType(Class<?> type) {
        if (type.isArray()) {
            return appendType(type.getComponentType()) + "[]";
        } else {
            return StringHelper.startsWithAny(type.getName(), "java.", "javax.") ? type.getSimpleName()
                    : type.getName();
        }
    }

    /**
     * 查找符合条件的第一个方法
     * 
     * @param clazz 类型
     * @param predicate 筛选条件
     * @param excludeInheritedMethods 是否排除父类的方法
     * @return
     */
    public static Optional<Method> getMethods(Class<?> clazz, Predicate<Method> predicate,
            boolean excludeInheritedMethods) {
        Method[] methods = excludeInheritedMethods ? clazz.getDeclaredMethods() : clazz.getMethods();
        return Stream.of(methods)
                .filter(predicate)
                .findFirst();
    }

    /**
     * 查找符合条件的所有方法,返回方法迭代器
     * 
     * @param clazz 类型
     * @param predicate 筛选条件
     * @param excludeInheritedMethods 是否排除父类的方法
     * @return
     */
    public static Iterator<Method> findMethods(Class<?> clazz, Predicate<Method> predicate,
            boolean excludeInheritedMethods) {
        Method[] methods = excludeInheritedMethods ? clazz.getDeclaredMethods() : clazz.getMethods();
        return Stream.of(methods)
                .filter(predicate)
                .iterator();
    }

}
